import { addOrder, delOrder, getOrderList, OrderData, OrderDataItem, OrderDetail, Process, Source, updateOrder } from "@/api/order"
import { Modal } from "@/components/Model"
import { Origin } from "@/types"
import { listenIdpMsgOnce, waitTime } from "@/utils"
import { PlankLayout } from "@/views/PlankLayout"
import { Button, Card, Divider, Dropdown, Form, InputNumber, MenuProps, message, Select, Space, Table, TableProps, Tooltip, Typography } from "antd"
import { DownOutlined, InfoCircleOutlined, QuestionCircleOutlined } from "@ant-design/icons";
import { FormInstance } from "antd/es/form"
import { cloneDeep, isEmpty, merge, omit, orderBy, pick, set, sum, times } from "lodash"
import moment from "moment"
import { ComponentProps, FC, Fragment, ReactNode, useEffect, useRef, useState } from "react"
import * as XDAPI from "xd-plank-layout-api";
import * as XDDataAdapter from "xd-data-adapter";
import axios from "axios"
import { designDataResultWrap, mergeDesignDataResult, transformPlanks, transformPlanksPath } from "model-utils";
import { CustomMaterialConfigDataType, CustomMaterialConfigTable } from "custom-material-config";
import { PlankChangeDataType, PlankChangeExtendForm, PlankChangeTable, usePlankChangeExtendForm } from "plank-change";
import { useRequest } from "ahooks";
import * as PlankChangeAdapter from "plank-change-adapter";
import { itemToArray } from "@/utils/data"
import { createShouldDebug } from "@/utils/debug"

// console.log({ XDAPI, XDDataAdapter });
const shouldDebug = createShouldDebug({ enableDebug: true, debugScope: { output: true } })

async function getPlankList(params: GetDesignDataParams & { getDesignName?(designName: string): void } = {}) {
  console.time('获取方案耗时')
  const data = await getDesignData(omit(params, 'getDesignName'))
  if (isEmpty(data)) {
    throw new Error('方案数据为空，请检查后重试')
  }
  params.getDesignName?.(data[0].designData.designName)
  console.log('方案数据', data);
  console.timeEnd('获取方案耗时')
  // const planks = transformPlanks(mergeDesignDataResult(designDataResultWrap(data)))
  console.time('处理板件耗时')
  const allPlanks: any[] = []
  const result: any[] = []
  for (const item of data) {
    const planks = transformPlanks(mergeDesignDataResult(designDataResultWrap([item])))
    const transformPlanksPathParams = planks.map(p => pick(p.context.m, 'paramPlankPath'))
    // const res = await listenIdpMsgOnce<{ points: any[] }[]>({ action: 'transformPlanksPath', value: transformPlanksPathParams })
    const res = transformPlanksPath(transformPlanksPathParams)
    res.forEach(p => p.points.reverse())
    allPlanks.push(...merge(cloneDeep(planks).map(p => merge(p)), res))
    const planks2 = merge(planks.map(p => merge(omit(p, 'context'))), res)
    result.push(...planks2)
  }
  console.log('板件数据', allPlanks);
  console.timeEnd('处理板件耗时')
  return result
}
interface GetDesignDataParams {
  /** 工具线 */
  toolTypes?: ToolType[],
  /** 只获取订单模型 */
  onlyOrderModel?: boolean
}
enum ToolType {
  Cabinet = 'Cabinet',
  Wardrobe = 'Wardrobe',
  DoorWindow = 'DoorWindow',
}
async function getDesignData(params: GetDesignDataParams = {}) {
  const data = await listenIdpMsgOnce<{ url: string }[], GetDesignDataParams>({ action: 'getDesignData', value: params })
  // const data = mockDesignData
  const result = await Promise.all(
    data.map(async item => await axios(item.url).then(res => res.data))
  )
  return result
}

const processMap = new Map([
  [Process.Wait, '排版中'],
  [Process.Finish, '已完成'],
])
enum SourceExtend {
  Cabinet = ToolType.Cabinet,
  Wardrobe = ToolType.Wardrobe,
  DoorWindow = ToolType.DoorWindow,
  OnlyOrderModel = 'OnlyOrderModel'
}
const sourceExtendMap = new Map([
  [SourceExtend.Cabinet, '厨柜'],
  [SourceExtend.Wardrobe, '衣柜'],
  [SourceExtend.DoorWindow, '门窗'],
  [SourceExtend.OnlyOrderModel, '订单产品'],
])
const sourceMap = new Map([
  [Source.CurrentDesign, '当前设计方案'],
])

async function updateConfig(data: PlankLayoutConfigFields) {
  // console.log("updateConfig", data);
  const configStr = JSON.stringify(data)
  localStorage.setItem('config', configStr)
  // await waitTime(3000)
}
function getConfig() {
  const config = localStorage.getItem('config')
  if (!config) return { ...defaultConfig }
  const configObj: PlankLayoutConfigFields = JSON.parse(config)
  // console.log({ config: configObj });
  return { ...defaultConfig, ...configObj }
}

const defaultConfig = {
  Algorithm: 1,
  Space: 6,
  TrimLeft: 10,
  TrimRight: 10,
  TrimFront: 10,
  TrimBack: 10,
  AlignPoint: 0,
  RemainSide: 0,
  DoubleFaceFirst: 0,
  EdgeBandingExtend: 0
}
function initConfig() {
  const config = getConfig()
  if (config) return
  updateConfig(defaultConfig)
}
initConfig()

const Toolbar: FC<{ getDesignName?: (designName: string) => void, onCurrentDesign(data: ReturnType<typeof XDDataAdapter.transformToInputData>, config: PlankLayoutConfigFields, source: Source | SourceExtend): void }> = (props) => {
  const onClickCurrentDesign = async (params: GetDesignDataParams = {}) => {
    const hide = message.loading('当前设计方案数据加载中，请稍后...', 0);
    try {
      console.log("当前设计方案")
      const plankList = await getPlankList(merge({}, params, pick(props, 'getDesignName')))
      if (isEmpty(plankList)) {
        message.error('方案内无对应产品，请检查！')
      }
      // console.log({ plankList });
      const config = getConfig()
      if (!config) throw new Error('config error')
      hide()
      // console.log({ inputData });
      const data = XDDataAdapter.transformToInputData({
        settings: { ...omit(config, 'EdgeBandingExtend') },
        smallPlankDatas: (plankList as any[]).map<XDDataAdapter.SmallPlankData>((plank, index) => {
          return {
            id: 10000 + index,
            code: plank.code,
            size: plank.size,
            hasOutline: plank.hasOutline,
            textureAngle: plank.textureAngle,
            modelProduct: plank.modelProduct,
            materialId: plank.materialId,
            material: plank.textureName,
            thickness: plank.thickness,
            vertices: plank.points,
            materialSize: plank.materialSize,
            name: plank.name,
            edgeLength: plank.edgeLength,
            edgeLengths: plank.edgeLengths,
            modelId: plank.modelId,
            topModelName: plank.topModelName,
            baseTexture: plank.baseTexture
          }
        })
      })
      console.log({ data, config });
      // if (isDebug) {
      //   return
      // }
      const { toolTypes, onlyOrderModel } = params
      const toolType = toolTypes?.[0]
      let source: Source | SourceExtend = Source.CurrentDesign
      if (toolType) {
        source = SourceExtend[toolType]
      } else if (onlyOrderModel) {
        source = SourceExtend.OnlyOrderModel
      }
      props.onCurrentDesign(data, config, source)
    } catch (error: any) {
      console.error(error);
      message.error(<span>获取数据失败:<br />{transformMutilineMsg(error.message)}</span>, 3)
      hide()
    }

  }
  return (
    <div className="toolbar">
      <Space>
        <GetDesignDataButton onClick={onClickCurrentDesign} />
        <PlankLayoutConfig />
        <CustomMaterialConfig />
      </Space>
      <Divider />
    </div>
  )
}
const items: MenuProps['items'] = [
  {
    label: '只排厨柜',
    key: ToolType.Cabinet,
  },
  {
    label: '只排衣柜',
    key: ToolType.Wardrobe,
  },
  {
    label: '只排门窗',
    key: ToolType.DoorWindow,
  },
  {
    label: '只排订单产品',
    key: 'onlyOrderModel',
  },
];
interface GetDesignDataButtonProps {
  onClick: (params?: GetDesignDataParams) => void
}
function GetDesignDataButton({ onClick }: GetDesignDataButtonProps) {
  return <Dropdown.Button
    type="primary"
    icon={<DownOutlined />}
    menu={{
      items, onClick({ key }) {
        const toolType = ToolType[key]
        if (toolType) {
          onClick({ toolTypes: itemToArray(toolType) })
        } else if (key === 'onlyOrderModel') {
          onClick({ onlyOrderModel: true })
        }
      }
    }}
    onClick={() => onClick()}
  >
    当前设计方案
  </Dropdown.Button>
}
const CustomMaterialConfig = () => {
  const [open, setOpen] = useState(false)
  const configResult = useRequest(() => getCustomMaterialConfig())
  const config = configResult.data
  async function onConfigChange(config: CustomMaterialConfigDataType[]) {
    configResult.mutate(config)
    await setCustomMaterialConfig(config)
  }

  return (
    <>
      <Button type="primary" onClick={() => setOpen(true)}>自定义排版规格</Button>
      <Modal
        width={'100%'}
        title={<>
          <Space>
            自定义排版规格
            <Tooltip title="系统默认排版规格不符合预期时，可在排版页面双击替换成下方的自定义排版规格">
              <QuestionCircleOutlined />
            </Tooltip>
          </Space>
        </>}
        open={open}
        footer={null}
        onCancel={() => setOpen(false)}
      >
        <CustomMaterialConfigTable dataSource={config} onConfirm={onConfigChange} />
        <Typography.Paragraph>{transformMutilineMsg(`
          提示：
          1、【大板规格名称】可自定义
          2、【大板长1】、【大板宽1】、【大板纹路】必填
          3、仅当需要制定优先排满49尺再排48尺类似规则时，才填写【大板长2】和【大板宽2】时，且值必须大于对应的【大板长1】、【大板宽1】，不需要时不填
        `)}</Typography.Paragraph>
      </Modal>
    </>
  )
}
enum StoreKey {
  CustomMaterialConfig = 'CUSTOM_MATERIAL_CONFIG',
}
let customMaterialConfig: CustomMaterialConfigDataType[] = []
async function getCustomMaterialConfig(): Promise<CustomMaterialConfigDataType[]> {
  // console.log('getCustomMaterialConfig', customMaterialConfig);
  const customMaterialConfigResult = await listenIdpMsgOnce({ action: 'getStorageData', value: { key: StoreKey.CustomMaterialConfig } })
  customMaterialConfig = customMaterialConfigResult?.value ?? []
  return customMaterialConfig
}
async function setCustomMaterialConfig(config: CustomMaterialConfigDataType[]): Promise<void> {
  // console.log('setCustomMaterialConfig', config);
  customMaterialConfig = config
  return listenIdpMsgOnce({ action: 'setStorageData', value: { key: StoreKey.CustomMaterialConfig, data: { value: config } } })
}

function transformMutilineMsg(msg?: string): ReactNode[] | undefined {
  const nodes = msg?.split('\n').reduce((nodes: ReactNode[], line, i) => {
    nodes.push(<Fragment key={i}>{line}<br /></Fragment>)
    return nodes
  }, [])
  return nodes
}
const OrderTable: FC<Omit<TableProps<OrderDataItem>, 'columns'> & { onPreview: (id: string) => void, onDelete: (id: string, record) => void }> = (props) => {
  const columns = [
    {
      title: '序号',
      dataIndex: 'order',
      render: (text, record, index) => index + 1
    },
    {
      title: '任务名称',
      dataIndex: 'designName',
    },
    {
      title: '任务日期',
      dataIndex: 'date',
      render: (text) => moment(text).format("YYYY-MM-DD HH:mm:ss")
    },
    {
      title: '来源',
      dataIndex: 'source',
      render: (text) => sourceMap.get(text) ?? sourceExtendMap.get(text)
    },
    {
      title: '排版进度',
      dataIndex: 'process',
      render: (text) => processMap.get(text)
    },
    // {
    //   title: '主材利用率',
    //   dataIndex: 'ratio',
    //   render: (text) => text && percentage(text)
    // },
    // {
    //   title: '主材张数',
    //   dataIndex: 'count',
    // },
    // {
    //   title: '排版耗时（分钟）',
    //   dataIndex: 'time',
    //   render: (text) => text && Math.round(moment.duration(text).asMinutes() * 10) / 10
    // },
    {
      title: '操作',
      key: 'action',
      render: (text, record) => (
        <Space>
          <Button type="text" disabled={record.process !== Process.Finish} onClick={() => {
            console.log("preview", record.id)
            props.onPreview(record.id)
          }
          }>排版图预览</Button>
          <Button type="text" onClick={() => {
            console.log("del", record.id)
            props.onDelete(record.id, record)
          }
          }>删除</Button>
        </Space >
      ),
    },
  ];
  const tableProps = omit(props, 'columns', 'onPreview', 'onDelete')
  return <Table {...tableProps} rowKey='id' dataSource={props.dataSource} columns={columns} />
}
// reset form fields when modal is form, closed
const useResetFormOnCloseModal = ({ form, visible }: { form: FormInstance; visible: boolean }) => {
  const prevVisibleRef = useRef<boolean>();
  useEffect(() => {
    prevVisibleRef.current = visible;
  }, [visible]);
  const prevVisible = prevVisibleRef.current;

  useEffect(() => {
    if (!visible && prevVisible) {
      form.resetFields();
    }
  }, [form, prevVisible, visible]);
};

interface PlankLayoutConfigFields {
  Algorithm: number;
  Space: number;
  TrimLeft: number;
  TrimRight: number;
  TrimFront: number;
  TrimBack: number;
  AlignPoint: number;
  RemainSide: number;
  DoubleFaceFirst: number;
  EdgeBandingExtend: number;
}

const AlgorithmOptions = [
  { label: '开启', value: 1 },
  { label: '关闭', value: 0 },
]
const originOptions = [
  { label: '左下', value: Origin.LeftDown },
  { label: '左上', value: Origin.LeftTop },
  { label: '右上', value: Origin.RightTop },
  { label: '右下', value: Origin.RightDown },
]
const remainSideOptions = [
  { label: '宽边', value: 0 },
  { label: '长边', value: 1 },
  { label: '无优先', value: 2 },
]


const PlankLayoutConfig: FC = () => {
  const [visible, setVisible] = useState(false);
  const [confirmLoading, setConfirmLoading] = useState(false);
  const [form] = Form.useForm();
  const config = getConfig()

  useResetFormOnCloseModal({
    form,
    visible,
  });

  const showModal = () => {
    setVisible(true);
  };
  const handleOk = () => {
    form.submit()
  };
  const handleCancel = () => {
    setVisible(false);
  };
  const onFinish = async (values: PlankLayoutConfigFields) => {
    setConfirmLoading(true)
    // console.log('values:', values);
    const newConfig = { ...defaultConfig, ...values }
    // console.log('config:', config);
    await updateConfig(newConfig)
    setConfirmLoading(false)
    setVisible(false)
  };

  return (
    <>
      <Button type="primary" onClick={showModal}>
        排版设置
      </Button>
      {/* {JSON.stringify(config)} */}
      <Modal
        title="排版设置"
        visible={visible}
        onOk={handleOk}
        confirmLoading={confirmLoading}
        onCancel={handleCancel}
      >
        <Form form={form} onFinish={onFinish} initialValues={config}>
          <Form.Item name="Algorithm" label="异形排版" tooltip="异形小板是否开启异形算法。开启：按异形真实轮廓排版；关闭：按矩形尺寸排版">
            <Select options={AlgorithmOptions} />
          </Form.Item>
          <Form.Item name="Space" label="小板间距" >
            <InputNumber />
          </Form.Item>
          <Form.Item name="TrimLeft" label="大板左修边" >
            <InputNumber />
          </Form.Item>
          <Form.Item name="TrimRight" label="大板右修边" >
            <InputNumber />
          </Form.Item>
          <Form.Item name="TrimFront" label="大板前修边" >
            <InputNumber />
          </Form.Item>
          <Form.Item name="TrimBack" label="大板后修边" >
            <InputNumber />
          </Form.Item>
          <Form.Item name="AlignPoint" label="大板原点" >
            <Select options={originOptions} />
          </Form.Item>
          <Form.Item name="RemainSide" label="密排边优先" >
            <Select options={remainSideOptions} />
          </Form.Item>
          <Form.Item name="EdgeBandingExtend" label="封边余量" tooltip="实际封边工序存在齐头切断余量，按需填写封边单侧的加工余量" >
            <InputNumber />
          </Form.Item>
        </Form>
      </Modal>
    </>
  );
}

export default function OrderPage() {
  const [orderId, setOrderId] = useState<string>()
  const [loading, setLoading] = useState<boolean>()
  const [orderList, setOrderList] = useState<OrderData>([])
  const [orderDetail, setOrderDetail] = useState<OrderDetail>()
  const [isModalVisible, setIsModalVisible] = useState(false);
  const designNameRef = useRef<string>("")

  const handleCancel = () => {
    setIsModalVisible(false);
  };

  async function fetchData() {
    setLoading(true)
    const orderList = await getOrderList()
    console.log({ orderList });
    setOrderList(orderBy(orderList, 'date', 'desc'))
    setLoading(false)
  }
  async function handlePreview(id: string) {
    setOrderId(id)
    // setOrderDetail(await getOrderDetail(id))
    const order = orderList.find((order) => order.id === id)
    //@ts-ignore
    const inputData = order?.taskData?.config
    const layoutDatas = order?.taskData?.result.objects
    if (!(inputData && layoutDatas)) return
    const orderDetail = { designName: order.designName!, inputData, layoutDatas }
    setOrderDetail(orderDetail)
    setIsModalVisible(true)
  }
  async function handleDelete(id: string, record) {
    await delOrder(id)
    await fetchData()
  }
  async function handleAddOrder(data: ReturnType<typeof XDDataAdapter.transformToInputData>, config: PlankLayoutConfigFields, source: Source | SourceExtend) {
    console.log('handleAddOrder');
    if (data) {
      console.log('data:', data);
      console.time('排版接口耗时')
      // if (shouldDebug('out')) {
      //   return
      // }
      const taskData = /* shouldDebug('output') ? await Promise.resolve().then(async () => (await waitTime(1000), JSON.parse(XDAPI.MockData.res.result))) :  */await XDAPI.getPlankLayout({ jsonData: JSON.stringify(data.xdInputData) }).then(async res => {
        if (res.status !== 2000) {
          throw new Error(res.msg)
        }
        const taskData = JSON.parse(res.result)
        return taskData
      }).catch(err => {
        console.error(err);
        const errMsg = '任务添加失败'
        message.error(`${errMsg}: ${err.message}`)
        throw new Error(errMsg, { cause: err })
      })
      const { id: orderId } = await addOrder({
        date: new Date().getTime(),
        source,
        process: Process.Wait
      })
      console.timeEnd('排版接口耗时')
      console.log({ orderId });
      taskData.config = config
      taskData.result = { objects: XDDataAdapter.transformOutputData({ outputData: taskData, extendConfig: pick(config, 'EdgeBandingExtend'), context: { transformContext: data.transformContext } }) }
      await updateOrder({
        id: orderId,
        designName: designNameRef.current,
        taskData,
        inputData: data.xdInputData,
        context: data.transformContext,
        process: Process.Finish
      })
      await fetchData()
    }
  }
  const addOrderParamsRef = useRef<Parameters<typeof handleAddOrder>>()
  const [plankChangeModelOpen, setPlankChangeModelOpen] = useState(false)
  const onPlankChangeConfirm = () => {
    const data = addOrderParamsRef.current![0]
    const { xdInputData, transformContext } = data
    const plankChangeExtend = getPlankChangeExtendFieldsValue()
    // console.log('debug', 'onPlankChangeConfirm', { plankChangeData });
    const xdInputDataNew = PlankChangeAdapter.getXDInputDataNew({ plankChangeData: plankChangeData!, plankChangeExtend, xdInputData, transformContext, customMaterialConfig, })
    data.xdInputData = xdInputDataNew
    const { hasSizeError } = onXDInputDataChange({ xdInputData: xdInputDataNew, transformContext })
    if (hasSizeError) {
      return
    }
    setPlankChangeModelOpen(false)
    const orderParams: typeof addOrderParamsRef.current = merge([], addOrderParamsRef.current!, [{ xdInputData: xdInputDataNew }])
    handleAddOrder(...orderParams).catch(err => {
      const errMsg = '添加订单失败'
      message.error(errMsg)
      console.error(errMsg, err)
    })
  }
  function onXDInputDataChange({ xdInputData, transformContext }: { xdInputData: XDAPI.DataType.InputData, transformContext: XDDataAdapter.TransformContextRecord }) {
    const sizeErrorSmallPlankDatas = XDDataAdapter.checkSizeErrorFormXdInputData({ xdInputData, transformContext })
    if (!isEmpty(sizeErrorSmallPlankDatas)) {
      showSizeErrorMsg(sizeErrorSmallPlankDatas)
      setPlankChangeData(PlankChangeAdapter.getPlankChangeData({ xdInputData, transformContext, sizeErrorSmallPlankDatas }))
      return { hasSizeError: true }
    } else {
      closeSizeErrorMsg()
    }
    return { hasSizeError: false }
  }
  const onPlankChangeCancel = () => {
    setPlankChangeModelOpen(false)
  }

  const onCurrentDesign: ComponentProps<typeof Toolbar>['onCurrentDesign'] = (data, config, source) => {
    addOrderParamsRef.current = [data, config, source]
    const { xdInputData, transformContext } = data
    setPlankChangeData(PlankChangeAdapter.getPlankChangeData({ xdInputData, transformContext, sizeErrorSmallPlankDatas: [] }))
    onXDInputDataChange({ xdInputData, transformContext })
    setPlankChangeModelOpen(true)
  }

  useEffect(() => {
    fetchData()
  }, [])

  const [plankChangeExtendForm] = usePlankChangeExtendForm()
  function getPlankChangeExtendFieldsValue() {
    return plankChangeExtendForm.getFieldsValue()
  }
  function afterPlankChangeModalClose() {
    plankChangeExtendForm.resetFields()
  }
  const [plankChangeData, setPlankChangeData] = useState<PlankChangeDataType[]>()
  const getBigPlankSize = () => {
    return customMaterialConfig.map(item => ({
      ...item,
      length2: item.length2 ?? item.length1,
      width2: item.width2 ?? item.width1
    }))
  }

  return (
    <div>
      <Modal width={'100%'} title='排版调整' open={plankChangeModelOpen} onOk={onPlankChangeConfirm} onCancel={onPlankChangeCancel} afterClose={afterPlankChangeModalClose} >
        <PlankChangeExtendForm form={plankChangeExtendForm} />
        <PlankChangeTable dataSource={plankChangeData} onChange={(value) => setPlankChangeData([...value])} getBigPlankSize={getBigPlankSize} />
      </Modal>
      <Card bordered={false}>
        <Card>
          <Toolbar getDesignName={designName => designNameRef.current = designName} onCurrentDesign={onCurrentDesign} />
          <OrderTable loading={loading} dataSource={orderList} onPreview={handlePreview} onDelete={handleDelete} />
        </Card>
        <Modal fullScreen title='排版图预览' visible={isModalVisible} onCancel={handleCancel} footer={false} >
          {orderId &&
            <PlankLayout dataSource={orderDetail} />
          }
        </Modal>
      </Card>
    </div>
  )
}

let _closeSizeErrorMsg: (() => void) | null = null
const closeSizeErrorMsg = () => {
  if (_closeSizeErrorMsg) {
    _closeSizeErrorMsg()
    _closeSizeErrorMsg = null
  }
}
function showSizeErrorMsg(sizeErrorSmallPlankDatas: XDDataAdapter.SmallPlankData[]) {
  const errMsg = XDDataAdapter.getSizeErrorMsg(sizeErrorSmallPlankDatas)
  closeSizeErrorMsg()
  _closeSizeErrorMsg = message.error({ content: <span>获取数据失败:<br />{transformMutilineMsg(errMsg)}</span>, duration: 3 })
}